/***
 * File  	: SnowflakeRenderer.java
 * Subject	: Generates the rendering scene to draw the snowflake
 * Author 	: Isaac Parenteau
 * Date		: March 30th, 2010
 */
package src.project.koch;
import java.util.ArrayList;
import java.util.List;

import javax.microedition.khronos.egl.EGLConfig;
import javax.microedition.khronos.opengles.GL10;

import android.opengl.GLSurfaceView;

public class SnowflakeRenderer implements GLSurfaceView.Renderer{

	private final float LENGTH = 1.5f;
	
	private List<Triangle> triangles;

	private int iterations = -1;
	private int delay = 500;
	private int maxIterations = 4;
	private int x = 0;
	private int y = 0;
	private int z = 0;
	
	private boolean restarting = false;
	private boolean isDrawing = false;
	private boolean rotate = false;
	
	private float bgRed = 0.0f;
	private float bgBlue = 0.0f;
	private float bgGreen = 0.0f;
	private float bgAlpha = 0.0f;
	
	private float red = 1.0f;
	private float green = 1.0f;
	private float blue = 1.0f;
	private float alpha = 1.0f;

	
	/**
	 * Constructor
	 */
	public SnowflakeRenderer()
	{	
		triangles = new ArrayList<Triangle>();
	}
	
	/**
	 * Changes the background color
	 * @param red
	 * @param green
	 * @param blue
	 */
	public void ChangeBackgroundColor(float r, float g, float b)
	{
		ChangeBackgroundColor(r, g, b, 1.0f);
	}
	
	/**
	 * Changes the background color with alpha value
	 * @param red
	 * @param green
	 * @param blue
	 * @param alpha
	 */
	public void ChangeBackgroundColor(float r, float g, float b, float a) {	
		bgRed = r;
		bgGreen = g;
		bgBlue = b;
		bgAlpha = a;
	}
	
	/**
	 * Changes the triangle color
	 * @param r
	 * @param g
	 * @param b
	 */
	public void ChangeTriangleColor(float r, float g, float b){
		ChangeTriangleColor(r, g, b, 1.0f);
	}
	
	/**
	 * Changes the triangle color with alpha value
	 * @param r
	 * @param g
	 * @param b
	 * @param a
	 */
	public void ChangeTriangleColor(float r, float g, float b, float a){
		red = r;
		green = g;
		blue = b;
		alpha = a;
	}
	
	/**
	 * Changes the delay between draws
	 * @param del
	 */
	public void ChangeDelay(int del){
		delay = del;
	}
	
	/**
	 * Changes the max number of iterations that the triangle 
	 * draws
	 * @param maxIters
	 */
	public void ChangeMaximumIterations(int maxIters){
		maxIterations = maxIters;
	}
	
	/**
	 * Sets the flag to allow the triangles to rotate
	 * @param isRotating
	 */
	public void Rotate(boolean xRotate, boolean yRotate, boolean zRotate){
		
		if(xRotate == true || yRotate == true || zRotate == true){
			rotate = true;
			
			x = xRotate == true ? 1 : 0;
			y = yRotate == true ? 1 : 0;
			z = zRotate == true ? 1 : 0;
		}
		else
			rotate = false;
	}
	
	/**
	 * Restarts the renderer
	 */
	public void Restart()
	{
		/*
		 * Since draw is constantly happening the flags are
		 * used to stop the triangles and iteration count
		 * from updating so it can be modified. Without
		 * the flags then it will throw a error stating
		 * that the triangle list was being modified 
		 * when it was also trying to be drawn
		 */
		restarting = true;
		if(isDrawing == false && restarting == true){
			iterations = -1;
			triangles.clear();
			restarting = false;
		}
	}
	@Override
	public void onDrawFrame(GL10 gl) {
        if(restarting)
        	return;
		
        //Sets the drawing flag
        isDrawing = true;
        
        // define the color we want to be displayed as the "clipping wall"
        gl.glClearColor(bgRed, bgGreen, bgBlue, bgAlpha);
        // clear the color buffer to show the ClearColor we called above...
        gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
        
        //Starts the renderer from calculating the triangle position
        Begin(gl);
        
        //Rotates the triangle
        if(rotate)
        	gl.glRotatef(3.0f, x, y, z);
        
		//Loops through the triangles and draws them on screen
		for(Triangle t : triangles)
			t.Draw(gl);
		
		try {
			Thread.sleep(delay);
		} 
		catch (InterruptedException e) {}
		
		isDrawing = false;
		
		if(restarting == true)
			Restart();

	}

	@Override
	public void onSurfaceChanged(GL10 gl, int width, int height) {
		//Sets the view port if the surface changes
		gl.glViewport(0, 0, width, height);
		
	}

	@Override
	public void onSurfaceCreated(GL10 gl, EGLConfig config) {
		//Enables writing to a vertex array for drawing vertices on screen
		gl.glEnableClientState(GL10.GL_VERTEX_ARRAY);
		
	}

	/**
	 * Begins the loop to 
	 * @param gl
	 */
	private void Begin(GL10 gl) {
		
		if(iterations == maxIterations)
			return;
		
		iterations++;
		triangles.clear();
		Start();
	}

	/**
	 * Start of the loop using recursive call back to find the
	 * vertices of the triangles for the snowflake
	 */
	private void Start() {
		//Starting points of the triangle
		//all points are based off from PointA
		float[] pointA = {-0.75f, -0.5f};
		float[] pointB = {pointA[0]+ LENGTH, pointA[1]};
		float[] pointC = {(float) (pointA[0] + 0.5 * LENGTH), 
				(float) (0.5 * LENGTH * Math.sqrt(3.0f) + pointA[1])};

		Triangle t = new Triangle(pointA, pointB, pointC);
		t.SetColor(red, green, blue, alpha);
		triangles.add(t);

	    drawNextTriangle(pointB, pointA, iterations);
	    drawNextTriangle(pointA, pointC, iterations);
	    drawNextTriangle(pointC, pointB, iterations);
		
	}

	/**
	 * Recursive loop to determine where each triangle should be drawn
	 * @param start
	 * @param end
	 * @param i
	 */
	private void drawNextTriangle(float[] start, float[] end, int i) {
		if(i == 0)
	        return;
	    
	    float deltaX = end [0] - start[0];
	    float deltaY = end[1] - start[1];

	    //factor finds the height based off the angle bisectors (halfWay) and
	    //the distance between the x coordinates and the y coordinates
	    float factor = (float) (Math.sqrt(3.0f) / 6);

	    float[] halfWay = {(end[0] + start[0]) / 2, (end[1] + start[1]) / 2};
	    float[] a = {(2 * end[0] + start[0]) / 3, (2 * end[1] + start[1]) / 3};
	    float[] b = {halfWay[0] - factor * deltaY, halfWay[1] + factor * deltaX};
	    float[] c = {(end[0] + 2 * start[0]) / 3, (end[1] + 2 * start[1]) / 3};
	    
		Triangle t = new Triangle(a, b, c);
		t.SetColor(red, green, blue, alpha);
		triangles.add(t);
	    
	    drawNextTriangle(a, end, i - 1);
	    drawNextTriangle(b, a, i - 1);
	    drawNextTriangle(start, c, i - 1);
	    drawNextTriangle(c, b, i - 1);	
	}
}
